package domain

import (
	"github.com/lab-online/pkg/auth"
	"github.com/lab-online/pkg/util"

	"github.com/lab-online/internal/class/constant"
	"github.com/lab-online/internal/class/entity"
	"github.com/lab-online/internal/shared/code"
	"github.com/lab-online/internal/shared/valueobject"
)

func (d *Domain) UpdateStudent(
	patches []valueobject.PatchVO,
	classEntity entity.ClassEntity,
	authEntity auth.AuthEntity,
) (entity.ClassEntity, error) {
	classEntity, dbErr := d.repository.GetClassWithStudentsByID(classEntity.GetID())
	if dbErr != nil {
		return nil,
			code.ErrorPicker(dbErr, code.DatabaseClassNotFound, constant.DBErrClassNotFound)
	}

	if !classEntity.CanHandleClass(authEntity, false) {
		return nil, constant.RESTErrNoPermissionToUpdateClass
	}

	classEntity.ApplyStudentPatch(patches...)

	students, dbErr := d.userRepository.GetManyUsersByUsername(classEntity.GetStudents())
	if dbErr != nil {
		return nil,
			code.ErrorPicker(dbErr, code.DatabaseUserNotFound, constant.RESTErrUserNotFound)
	}

	notStudentVOs := util.SliceFilter(students, func(user valueobject.UserVO) bool {
		return !user.IsStudent()
	})
	switch {
	case len(notStudentVOs) != 0:
		classEntity.UpdateEntity(entity.WithStudentVOs(notStudentVOs))
		return classEntity, constant.RESTErrUserIsNotStudent
	case len(students) != len(classEntity.GetStudentVOs()):
		notExistUsers := getNotExistUsers(students, classEntity.GetStudents())
		classEntity.UpdateEntity(entity.WithStudents(notExistUsers...))
		return classEntity, constant.RESTErrUserNotFound
	}

	classEntity.UpdateEntity(entity.WithStudentVOs(students))
	_, err := d.repository.SaveWithStudents(classEntity)
	return util.ConditionalPicker(err == nil, classEntity, nil), err
}

func getNotExistUsers(userVOs []valueobject.UserVO, users []string) []string {
	existStudentMap := make(map[string]struct{}, len(userVOs))
	for _, user := range userVOs {
		existStudentMap[user.GetUsername()] = struct{}{}
	}

	return util.SliceFilter(users, func(user string) bool {
		_, exist := existStudentMap[user]
		return !exist
	})
}
